#!/usr/bin/env python

# Big block of hackish code. Update description later.

# -------------
# PANDA IMPORTS
# -------------
import direct.directbase.DirectStart
from panda3d.core import CollisionTraverser,CollisionNode
from panda3d.core import CollisionHandlerQueue,CollisionSphere,CollisionRay
from panda3d.core import Filename,AmbientLight,DirectionalLight
from panda3d.core import PandaNode,NodePath,Camera,TextNode
from panda3d.core import Vec3,Vec4,BitMask32
from pandac.PandaModules import MovieTexture
from direct.gui.OnscreenText import OnscreenText
from direct.actor.Actor import Actor
from direct.showbase.DirectObject import DirectObject
from direct.interval.IntervalGlobal import *
from pandac.PandaModules import ClockObject
from pandac.PandaModules import WindowProperties
from pandac.PandaModules import CardMaker
from pandac.PandaModules import TextureStage
import Queue

# -------------------
# OTHER (OUR) IMPORTS
# -------------------
import clip_plane
import text_writers
import enemies
import effect
import random, sys, os, math, time

# ------------------------
class World(DirectObject):
# ------------------------

    # ---------------------------
    # BEGINNING OF INITIALIZATION
    # ---------------------------
    def __init__(self):

        # ------------------------------------------
        # WORLD CONSTANTS DECLARATION AND DEFINITION
        # ------------------------------------------

        # is Ralph moving at the moment? Used for animation
        self.isMoving = False

        # For Determining Time Between Frames
        self.startTime = 0
        self.lastTime = globalClock.getFrameTime()
        self.dTime = 0

        # Movement Speed
        self.WALKSPEED = 16
        self.RUNSPEED = 25

        # Jumping/Falling Constants
        self.GRAVITY = 3
        self.JUMPVECTOR = 13.5
        self.MAXDOWNSPEED = -100
        self.MAXJUMPTIME=0.4
        self.upSpeed = 0
        self.downForce = 0
        self.jumpTime = 0
        self.ZBOUND = -15

        # How far Ralph is from ground to snap to it
        self.GROUNDSNAP = 0.5

        # --------------------------
        # KEY MAPPING AND ASSIGNMENT
        # --------------------------

        self.keyMap = {"left":0, "right":0, "forward":0, "backward":0, 
                                "turned":0, "running":0, "jumping":0, "grounded":0,
                                   "noJump":0, "constantJump":0, "constantRun":0, "frozen":0, "noLook":0}
        self.accept("escape", sys.exit)
        self.accept("a", self.setKey, ["running",1])
        self.accept("mouse1", self.setKey, ["turned",1])
        self.accept("s", self.setKey, ["backward",1])
        self.accept("w", self.setKey, ["forward",1])
        self.accept("c", self.toggleCameraMode)
        self.accept("v", self.toggleViewCollisions)
        self.accept("+", self.changeCameraFov, [1.0])
        self.accept("-", self.changeCameraFov, [-1.0])
        self.accept("p", self.printDebug)
        self.accept("space", self.setKey, ["jumping",1])
        self.accept("a-up", self.setKey, ["running",0])
        self.accept("w-up", self.setKey, ["forward",0])
        self.accept("mouse1-up", self.setKey, ["turned",0])
        self.accept("space-up", self.setKey, ["jumping",0])
        self.accept("s-up", self.setKey, ["backward",0])

        # PYTHON TASKS -- IMPORTANT!
        taskMgr.add(self.move,"moveTask")
        taskMgr.add(self.cameraTask,"cameraTask")
        taskMgr.add(self.updateTimeTask, "updateTimeTask")
        taskMgr.add(self.checkWin,"checkWinTask")
        taskMgr.add(self.effectController,"effectController")

        # -------------------
        # ENVIRONMENT LOADING
        # -------------------

        # Makes the non-textured world environment black (aka "clear" color)
        base.win.setClearColor(Vec4(0,0,0,1))

        # Decide which world level to load
        self.level =  "../models/ground/level_1-1" #"../models/one-two/level_1-2"
        self.environ = loader.loadModel(self.level) # LEVEL 1-1
        #self.environ = loader.loadModel(self.level) # LEVEL 1-2
        self.environ.reparentTo(render)
        self.environ.setPos(0,0,0)
        
        # -------------
        # ACTOR LOADING
        # -------------

        # Create and load the main character (at this moment, Ralph)
        self.ralph = Actor("../actors/ralph/ralph",
                                 {"run":"../actors/ralph/ralph-run", 
                                  "walk":"../actors/ralph/ralph-walk"})
        self.ralph.reparentTo(render) # renders main character
        self.ralph.setScale(.5) # scales character, if necessary
        self.ralph.setPos(0, 5, 8) # set position in current level
        self.ralph.setHpr(180.0, 0, 0) # infinite wave of light, set directionary on character
        
        #load for performance
        loader.loadModel("../models/coin/coin")

        # ------------------------------
        # CAMERA LOADING AND POSITIONING
        # ------------------------------

        # disable default mouse behavior / hide cursor
        base.disableMouse()
        props = WindowProperties()
        props.setCursorHidden(True) 
        base.win.requestProperties(props)

        # Set up the camera
        base.camera.setPos(self.ralph.getX(),self.ralph.getY(),self.ralph.getZ()) # position
        base.camLens.setFov(70.0)
        base.camLens.setFar(60)
        self.ralph.setHpr(self.ralph.getH(), self.ralph.getP(), self.ralph.getR()) # lighting

        # Where the camera floats above the grove
        self.CAMDISTANCE = 30 # distance for 3rd person
        self.YROTCONST = 60.0
        self.XROTCONST = -75.0
        self.CAM_ZEN_SCALE = math.pi / 8.0
        self.CAM_ZEN_OFFSET = - 3.0 * math.pi / 8.0
        self.CAM_AZ_SCALE = math.pi / 8.0
        self.CAM_AZ_OFFSET = 5.0 * math.pi / 8.0

        # First or third person perspective?
        self.CAMSTYLE = 1 # 1 = first person, 2 = third person
        self.ralph.hide() # hide ralph in first person

        # -----------------
        # LIGHTING HANDLING
        # -----------------

        ambientLight = AmbientLight("ambientLight")
        ambientLight.setColor(Vec4(.3, .3, .3, 1))
        directionalLight = DirectionalLight("directionalLight")
        directionalLight.setDirection(Vec3(-5, -5, -5))
        directionalLight.setColor(Vec4(1, 1, 1, 1))
        directionalLight.setSpecularColor(Vec4(1, 1, 1, 1))
        render.setLight(render.attachNewNode(ambientLight))
        render.setLight(render.attachNewNode(directionalLight))

        # ------------------------------------------
        # COLLISION HANDLING - ACTOR AND ENVIRONMENT
        # ------------------------------------------

        # View Collisions? (debugging parameter)
        self.VIEWCOL = 0

        # Does the actual work of checking all solid objects for collisions
        self.cTrav = CollisionTraverser()
        
        # Ground Level Sphere (on Ralph)
        self.ralphGroundSphereHandler = CollisionHandlerQueue()
        self.ralphGroundSphereColNp = self.setupSphereCollider(self.ralph, 0, 0, 2, 0.75, 
                'ralphGroundSphere', self.ralphGroundSphereHandler, True)
        

        # Head Level Sphere (on Ralph)
        self.ralphHeadSphereHandler = CollisionHandlerQueue()
        self.ralphHeadSphereColNp = self.setupSphereCollider(self.ralph, 0, 0, 5.0, 0.50, 
                'ralphHeadSphere', self.ralphHeadSphereHandler, True)
                
        # Enemy Contact Sphere (into Ralph)
        #self.ralphEnemySphereHandler = CollisionHandlerQueue()
        #self.ralphEnemySphereColNp = self.setupSphereCollider(self.ralph, 0, 0, 2, 4, 
        #        'ralphEnemySphere', self.ralphEnemySphereHandler, False)

        # Environment and Actor Ground Collision Rays
        self.ralphGroundRay = CollisionRay()
        self.ralphGroundRay.setOrigin(0,0,1000)
        self.ralphGroundRay.setDirection(0,0,-1)
        self.ralphGroundCol = CollisionNode('ralphRay')
        self.ralphGroundCol.addSolid(self.ralphGroundRay)
        self.ralphGroundCol.setFromCollideMask(BitMask32.bit(0))
        self.ralphGroundCol.setIntoCollideMask(BitMask32.allOff())
        self.ralphGroundColNp = self.ralph.attachNewNode(self.ralphGroundCol)
        self.ralphGroundRayHandler = CollisionHandlerQueue()
        self.cTrav.addCollider(self.ralphGroundColNp, self.ralphGroundRayHandler)

        # Renders actual collisions (debug) -- COMMENT THE BELOW LINE TO REMOVE     
        if (self.VIEWCOL == 1):
          self.cTrav.showCollisions(render)

        # ---------------------
        # ONSCREEN TEXT LOADING
        # ---------------------
        
        # Load Debugging Instructions
        self.DEBUGTEXT = False  # set to True to enable debug text
        self.instLoc = [0.90, 0.85, 0.80, 0.75, 0.70, 0.65]
        self.instructions = [None, None, None, None, None, None]
        if self.DEBUGTEXT:
            text_writers.setInst(self, 0, "[GAME STATE]")
            text_writers.setInst(self, 1, "[UPSPEED]")
            text_writers.setInst(self, 2, "[JUMPING?]")
            text_writers.setInst(self, 3, "[CAM AZ ZEN]")
            text_writers.setInst(self, 4, "[CAM X Y Z]")
            text_writers.setInst(self, 5, "Camera: first person")

	    # Initialize Death Text for Later Use
        self.deathText = text_writers.addCenteredRedTitle(self, "GAME OVER")
        self.deathText.hide(BitMask32.allOn())
        self.deathText2 = text_writers.addCenteredRedSubtitle(self, "Press ESC to Exit")
        self.deathText2.hide(BitMask32.allOn())
        
	    # Initialize Win Text for Later Use
        self.winText = text_writers.addCenteredRedTitle(self, "YOU WIN!")
        self.winText.hide(BitMask32.allOn())
        self.winText2 = text_writers.addCenteredRedSubtitle(self, "Press ESC to Exit")
        self.winText2.hide(BitMask32.allOn())

        # Initialize Clock
        self.clock = ClockObject()
        self.time = text_writers.addTimeElapsed(self)
        
        # ------------------------------
        # SOUND EFFECT AND MUSIC LOADING
        # ------------------------------

        self.jumpSound = base.loadMusic("../sounds/effects/mario_jump.wav")
        self.coinSound = base.loadMusic("../sounds/effects/coin.wav")
        self.breakBlockSound = base.loadMusic("../sounds/effects/smb_breakblock.wav")
        self.fireballSound = base.loadMusic("../sounds/effects/fireball.wav")
        self.deathSound = base.loadMusic("../sounds/effects/death.wav")
        self.gameoverSound = base.loadMusic("../sounds/effects/game_over.wav")
        self.winStageSound = base.loadMusic("../sounds/effects/win_stage.wav")

        #-----------------------
        # CLIPPING PLANE LOADING
        #-----------------------

        clip_plane.initClippingPlane(self)

        # ------------------------------------
        # ENEMY LOADING AND COLLISION HANDLING
        # ------------------------------------
        
        enemies.initEnemies(self, self.level)

        # ---------------------------
        # EXTRA REALITY PLANE EFFECTS
        # ---------------------------

        self.effectList = Queue.Queue(0)
        self.effectList.put(effect.Effect(90, 145, "constantRun", "Mom's upset...RUN!", False, 2))
        self.effectList.put(effect.Effect(170, 205, "noJump", "Phone Call!", False, 1))
        self.effectList.put(effect.Effect(270, 320, "constantJump", "POLAR BEAR ATTACK", False, 3))
        #self.effectList.put(effect.Effect(7, 8, "frozen", "Mom, watch the vacuum!", 10))
        

        self.currentEffect = self.effectList.get()
        
    # ------------------------- #
    #   END OF INITIALIZATION   #
    # ------------------------- #


    
    # -----------------------------
    def printDebug(self):
    # -----------------------------
        print "ralphYZ: {0}, {1}".format(self.ralph.getY(), self.ralph.getZ())

    # -----------------------------
    def changeCameraFov(self, offset):
    # -----------------------------
      base.camLens.setFov(base.camLens.getFov().getX() + offset)
      print base.camLens.getFov().getX()

    # -----------------------------
    def toggleViewCollisions(self):
    # -----------------------------
        if (self.VIEWCOL == 1):
            self.VIEWCOL = 0
            self.ralphGroundSphereColNp.hide()
            self.ralphHeadSphereColNp.hide()
            #self.ralphEnemySphereColNp.hide()
            self.goombaSphereColNp.hide()
            self.koopaSphereColNp.hide()
            self.ralphGroundColNp.hide()
        else:
            self.VIEWCOL = 1
            self.ralphGroundSphereColNp.show()
            self.ralphHeadSphereColNp.show()
            #self.ralphEnemySphereColNp.show()
            self.goombaSphereColNp.show()
            self.koopaSphereColNp.show()
            self.ralphGroundColNp.show()

    # -------------------------
    def toggleCameraMode(self):
    # -------------------------
        if (self.CAMSTYLE == 1):
            self.CAMSTYLE = 2
            if self.DEBUGTEXT: text_writers.setInst(self, 5, "Camera: third person")
            # show Ralph in third person
            self.ralph.show()

        else:
            self.CAMSTYLE = 1
            if self.DEBUGTEXT: text_writers.setInst(self, 5, "Camera: first person")
            # hide Ralph in first person
            self.ralph.hide()
            
    """ This method gets called by the collision setup code to create a sphere collider """
    # -----------------------------------------------------------------------
    def setupSphereCollider(self, target, x, y, z, r, name, handler, isFrom):
    # -----------------------------------------------------------------------
        sphere = CollisionSphere(x, y, z, r)
        sphereCol = CollisionNode(name)
        sphereCol.addSolid(sphere)
        if isFrom:
            sphereCol.setFromCollideMask(BitMask32(0x01))
            sphereCol.setIntoCollideMask(BitMask32(0x02))
            np = target.attachNewNode(sphereCol)
            self.cTrav.addCollider(np, handler)
        else:
            sphereCol.setFromCollideMask(BitMask32.allOff())
            sphereCol.setIntoCollideMask(BitMask32(0.03))
            np = target.attachNewNode(sphereCol)
            #self.cTrav.addCollider(np, handler) don't add into objects to traverser
        
        return np
        
        
    """ Records the state of the arrow keys """
    # ---------------------------
    def setKey(self, key, value):
    # ---------------------------
        self.keyMap[key] = value
    
    """ Task to control looking up and down with the mouse """
    # ------------------------
    def cameraTask(self,task):
    # ------------------------
        if (self.keyMap["noLook"] == 1):
            return task.cont
        
        if (self.CAMSTYLE == 1): # first person

            try: 
               # Get mouse coordinates from mouse watcher 
                if ( base.mouseWatcherNode.hasMouse() ):
                    yPos=base.mouseWatcherNode.getMouseY()
                    xPos=base.mouseWatcherNode.getMouseX()
                    camPitch = yPos * self.YROTCONST
                    camYaw = xPos * self.XROTCONST + self.ralph.getH() - 180.0
                    camRoll = base.camera.getR()
                    base.camera.setHpr(camYaw, camPitch, camRoll)
                    base.win.movePointer(0, center[0], center[1]) 
                
            except: 
                # If mouse goes outside window, catch the 
                #    exception here and pass gracefully. 
                pass 
            
            # always put camera where Ralph is
            base.camera.setPos(self.ralph.getX(), self.ralph.getY()+0.25, self.ralph.getZ()+3)
        elif (self.CAMSTYLE == 2): # third person

            try: 
               # Get mouse coordinates from mouse watcher 
                if ( base.mouseWatcherNode.hasMouse() ):
                    yPos=base.mouseWatcherNode.getMouseY()
                    xPos=base.mouseWatcherNode.getMouseX()
                    camAz = xPos * self.CAM_AZ_SCALE + self.CAM_AZ_OFFSET
                    camZen = yPos * self.CAM_ZEN_SCALE + self.CAM_ZEN_OFFSET
                    if self.DEBUGTEXT: text_writers.setInst(self, 3, "cam Az,Zen: {0}, {1}".format(camAz, camZen))
                    camX = self.CAMDISTANCE * math.sin(camZen) * math.cos(camAz)
                    camY = self.CAMDISTANCE * math.sin(camZen) * math.sin(camAz)
                    camZ = self.CAMDISTANCE * math.cos(camZen)
                    if self.DEBUGTEXT: text_writers.setInst(self, 4, "cam XYZ: {0}, {1}".format(camX, camY,camZ))
                    base.camera.setPos(self.ralph.getX() + camX,
                            self.ralph.getY() + camY, self.ralph.getZ() + camZ)
                    base.camera.lookAt(self.ralph)
                    base.win.movePointer(0, center[0], center[1]) 
                
            except: 
                # If mouse goes outside window, catch the 
                #    exception here and pass gracefully. 
                pass 
           
        return task.cont

    # -----------------------------
    def updateTimeTask(self, task):
    # -----------------------------
        self.time.destroy()
        msg = "Time Elapsed: %d" % (globalClock.getFrameTime())
        self.time = OnscreenText(text=msg, style=1, fg=(1,1,1,1), pos=(+1.3, 0.90),
                align=TextNode.ARight, scale = .05, mayChange=True)
        return task.cont

    """ Checks to see if we're at the flagpole """
    # ----------------------
    def checkWin(self, task):
    # ----------------------
      if (self.ralph.getY() >= 398.0): # TODO: change based on level
      # YOU WIN!
          self.win()

          #Stop moving when you win
          self.setKey("frozen", 1)
          self.setKey("noLook", 1)
          
          return task.done
      else:
          return task.cont

    # -------------------------------
    def effectController(self, task):
    # -------------------------------

        if (not(self.currentEffect.getEnabled())) and (self.ralph.getY() >= self.currentEffect.getStart()):
            self.currentEffect.setEnabled(True)
            self.setKey(self.currentEffect.getName(), 1)
            #print(self.currentEffect.getName() + " is enabled")
            self.effectText = text_writers.addCenteredEffectText(self, self.currentEffect.getText())
            clip_plane.changeVideo(self, self.currentEffect.getVidNum())
            
            if(self.currentEffect.getTime() != False):
                self.currentEffect.setStartTime(globalClock.getFrameTime())
                
        elif(self.currentEffect.getEnabled()) and ((self.ralph.getY() >= self.currentEffect.getEnd())
                                                   or ((self.currentEffect.getTime() != False)
                                                       and (globalClock.getFrameTime() - self.currentEffect.getStartTime() >= self.currentEffect.getTime()))):
            
            self.currentEffect.setEnabled(False)
            self.setKey(self.currentEffect.getName(), 0)
            print(self.currentEffect.getName() + " is disabled")
            self.effectText.hide(BitMask32.allOn())
            clip_plane.changeVideo(self, 0)

            #Special exception for constant run
            if(self.currentEffect.getName() == "constantRun"):
                self.setKey("forward", 0)
                self.setKey("running", 0)

            #Check for more effects, set the current effect to the appropriate one
            if(self.effectList.empty()):
                return task.done
            else:
                self.currentEffect = self.effectList.get()
                #print "New effect is " + self.currentEffect.getName()

        return task.cont
    
    """ Accepts arrow keys to move either the player or the menu cursor,
        Also deals with grid checking and collision detection """
    # -------------------
    def move(self, task):
    # -------------------
        
        # Get time delta
        # NOTE: this ends up being equal to globalClock.getDt() but
        # for mysterious unknown reasons if I try to use that in the
        # falling code, everything gets screwed up. -MDD
        curTime = globalClock.getFrameTime()
        self.dTime = curTime - self.lastTime
        self.lastTime = curTime

        # save ralph's initial position so that we can restore it,
        # in case he falls off the map or runs into something.
        startpos = self.ralph.getPos()

        # Constant Running Effect
        if(self.keyMap["constantRun"] == 1) and ((self.keyMap["running"] == 0) or (self.keyMap["forward"] == 0)):
            self.setKey("running", 1)
            self.setKey("forward", 1)
            
        # Are we walking or running?
        if (self.keyMap["running"]!=0):
            movespeed = self.RUNSPEED
        else:
            movespeed = self.WALKSPEED

        # Prevent ralph from moving or changing orientation when frozen
        if (self.keyMap["frozen"] == 0):
            
            # point ralph in the right direction
            if (self.keyMap["turned"]!=0) and (self.keyMap["constantRun"] == 0):
                self.ralph.setHpr(0.0, 0, 0)
            else:
                self.ralph.setHpr(180.0, 0, 0)

            # If a move-key is pressed, move ralph in the specified direction.
            if (self.keyMap["left"]!=0):
                self.ralph.setH(self.ralph.getH() + 300 * globalClock.getDt())
            if (self.keyMap["right"]!=0):
                self.ralph.setH(self.ralph.getH() - 300 * globalClock.getDt())
            if (self.keyMap["forward"]!=0):
                self.ralph.setY(self.ralph, -movespeed * globalClock.getDt())
                clip_plane.moveClippingPlane(self)
            if (self.keyMap["backward"]!=0):
                self.ralph.setY(self.ralph, movespeed * globalClock.getDt())
                clip_plane.moveClippingPlane(self)

            # If ralph is moving, loop the run animation.
            # If he is standing still, stop the animation.
            if (self.keyMap["forward"]!=0) or (self.keyMap["left"]!=0) or (self.keyMap["right"]!=0) or (self.keyMap["backward"]!=0):
                if self.isMoving is False:
                    self.ralph.loop("run")
                    self.isMoving = True
            else:
                if self.isMoving:
                    self.ralph.stop()
                    self.ralph.pose("walk",5)
                    self.isMoving = False

        # Jumping - initial
        if (self.keyMap["jumping"]!=0) and (self.keyMap["grounded"]!=0) and (self.keyMap["noJump"] == 0) and (self.keyMap["frozen"] == 0):
            if self.DEBUGTEXT: text_writers.setInst(self, 2, "JUMPING: {0}".format(self.keyMap["jumping"]))
            self.upSpeed = self.JUMPVECTOR
            if self.DEBUGTEXT: text_writers.setInst(self, 1, "upSpeed: {0}".format(self.upSpeed))
            self.jumpSound.play()

        # Now check for collisions.
        self.cTrav.traverse(render)
  
        # assume Ralph isn't grounded, unless we find otherwise 
        # in the next bit of code
        self.setKey("grounded", 0)

        # Adjust ralph's Z coordinate.  If ralph's ray hit terrain,
        # update his Z. If it hit anything else, or didn't hit anything, put
        # him back where he was last frame.
        if (self.keyMap["jumping"]==0):
            entries = []
            for i in range(self.ralphGroundRayHandler.getNumEntries()):
                entry = self.ralphGroundRayHandler.getEntry(i)
                entries.append(entry)
            entries.sort(lambda x,y: cmp(y.getSurfacePoint(render).getZ(),
                                         x.getSurfacePoint(render).getZ()))
            if (len(entries)>0):
                # find ground under Ralph
                for i in range(self.ralphGroundRayHandler.getNumEntries()):
                    groundDiff = entries[i].getSurfacePoint(render).getZ() - self.ralph.getZ() 
                    if (groundDiff < self.GROUNDSNAP) and (groundDiff > -self.GROUNDSNAP): 
                        # Ralph is close enough to the ground - lock him to it
                        self.ralph.setZ(entries[i].getSurfacePoint(render).getZ())
                        self.upSpeed = 0.0
                        if self.DEBUGTEXT: text_writers.setInst(self, 1, "upSpeed: {0}".format(self.upSpeed))
                        self.downForce = 0.0
                        self.setKey("grounded", 1)
                        if self.DEBUGTEXT: text_writers.setInst(self, 2, "JUMPING: {0}".format(self.keyMap["jumping"]))
            else:
                if self.DEBUGTEXT: text_writers.setInst(self, 0, "Out of bounds!")

        #
        # Sphere ground handler for wall collision
        #
        entries = []
        for i in range(self.ralphGroundSphereHandler.getNumEntries()):
            entry = self.ralphGroundSphereHandler.getEntry(i)
            entries.append(entry)
            
        if (len(entries)>0): 
            # Ralph has hit an obstacle, don't let him move forward
            self.ralph.setPos(startpos)
            if self.DEBUGTEXT: text_writers.setInst(self, 0, "Hit Obstacle")
            
        #
        # Sphere enemy handler for enemy collision
        #
        #print self.enemySphereHandler.getNumEntries()
        #print self.enemySphereHandler
        for i in range(self.enemySphereHandler.getNumEntries()):
            entry = self.enemySphereHandler.getEntry(i)
            
            if entry.hasInto() :
                if "Wall" in entry.getIntoNodePath().getName() :
                    #print entry
                    enemies.collideEnemiesIntoWall(self, entry)
                elif "enemy" in entry.getIntoNodePath().getName() :
                    # 
                    enemies.collideEnemiesIntoEnemies(self, entry)
                elif "ralph" in entry.getIntoNodePath().getName():
                    #print entry
                    enemies.collideEnemiesIntoRalph(self, entry)
            
            

        # Head sphere handler
        hitHead = 0
        entries = []
        for i in range(self.ralphHeadSphereHandler.getNumEntries()):
            entry = self.ralphHeadSphereHandler.getEntry(i)
            entries.append(entry)
            objX = entries[i].getSurfacePoint(render).getX()
            objY = entries[i].getSurfacePoint(render).getY()
            objZ = entries[i].getSurfacePoint(render).getZ()
            if (self.upSpeed > 0) and (objZ - self.ralph.getZ() > 0.0)\
            and (abs(objY - self.ralph.getY()) < 1.0):

                # the object was above Ralph; halt his upward velocity
                self.upSpeed = 0;

                # make sure he doesn't get stuck in the block
                self.ralph.setZ(objZ - 1.5)
                hitHead = 1

                # get name of block hit
                blockName = entries[i].getIntoNode().getName()
                # Testing removing the block
                print " > {0}".format(blockName)
                if blockName.startswith("QBlock") :
                    # coin block
                    self.coinSound.play()
                    entry.getIntoNode().setName("DeadQBlock")
                    entry.getIntoNodePath().getParent().hide() # hide block
                    entry.getIntoNodePath().detachNode() # rem collision
                    
                    
                    coin = loader.loadModel("../models/coin/coin")
                    coin.reparentTo(render)
                    coin.setPos(objX, objY, objZ)
                    coin.setScale(-1*self.goombaSX, self.goombaSY, self.goombaSZ)
                    
                    Sequence(
                        Parallel(LerpPosInterval(coin, .5, (objX, objY, objZ+5)),
                                LerpHprInterval(coin, .5, (360, 0, 0))),
                        Parallel(LerpPosInterval(coin, .5, (objX, objY, objZ)),
                                LerpHprInterval(coin, .5, (720, 0, 0))),
                        Func(self.killCoin, coin)).start()

                    
                elif blockName.startswith("Ground") :
                    # regular block
                    self.breakBlockSound.play()
                    entries[i].getIntoNodePath().getParent().hide() # hide block
                    entries[i].getIntoNodePath().detachNode() # rem collision
                
        if (not hitHead) and (len(entries)>0): 
            # Ralph has hit an obstacle, don't let him move forward
            self.ralph.setPos(startpos)
            if self.DEBUGTEXT: text_writers.setInst(self, 0, "Hit Obstacle")

        # make sure Ralph hasn't been jumping too long
        if (self.jumpTime > self.MAXJUMPTIME):
            self.setKey("jumping", 0)

        # gravity
        if (self.keyMap["grounded"] == 0):
            # Ralph is above the ground, make him fall
            self.ralph.setZ(self.ralph.getZ() + (self.upSpeed * self.dTime))
            # not if he is still jumping though
            if (self.keyMap["jumping"] == 0):
                self.downForce = self.downForce + (self.GRAVITY * self.dTime)
                self.upSpeed = self.upSpeed - self.downForce
                self.jumpTime = 0
            else:
                self.jumpTime += self.dTime
            if (self.upSpeed < 0.0): self.setKey("jumping", 0)
            if self.DEBUGTEXT: text_writers.setInst(self, 2, "JUMPING: {0}".format(self.keyMap["jumping"]))
            if (self.upSpeed < self.MAXDOWNSPEED): self.upSpeed = self.MAXDOWNSPEED
            if self.DEBUGTEXT: text_writers.setInst(self, 1, "upSpeed: {0}".format(self.upSpeed))

        # constant jumping effect
        
        if (self.keyMap["constantJump"] == 1) and (self.keyMap["grounded"] == 1):
            self.setKey("jumping", 1)
            
        # falling to death
        if (self.ralph.getZ() < self.ZBOUND):
            self.die() 
            return task.done
        else:
            return task.cont
    """ END of move() """

    """ Death screen """
    # ------------
    def die(self):
    # ------------
        #Stop moving when you die
        self.setKey("frozen", 1)
        self.setKey("noLook", 1)
        
        self.deathText.show(BitMask32.allOn())
        self.deathText2.show(BitMask32.allOn())
        self.playLoseVid()
        self.gameoverSound.play()
        #exit() # for now just end the program

    def playLoseVid(self):
        # init video texture for intro, and load the video onto it
        self.deathTex = MovieTexture("death")
        assert self.deathTex.read("../models/videos/lose1_new.avi"), "THE INTRO DID NOT LOAD!"
        self.deathSound=loader.loadSfx("../models/videos/lose1_new.avi")

        # set up a full screen card to play the video on
        cm = CardMaker("My Fullscreen Card");
        cm.setFrameFullscreenQuad()
        cm.setUvRange(self.deathTex)
        card = NodePath(cm.generate())
        card.reparentTo(render2d)
        card.setTexture(self.deathTex)
        card.setTexScale(TextureStage.getDefault(), self.deathTex.getTexScale())
        # Synchronize the video to the sound.
        self.deathTex.synchronizeTo(self.deathSound)
        self.deathSound.play()
        
    """ Win screen """
    # ------------
    def win(self):
    # ------------
        self.winText.show(BitMask32.allOn())
        self.winText2.show(BitMask32.allOn())
        self.winStageSound.play()
        #exit() # for now just end the program
        
    # ------------
    def killCoin(self, coin):
    # ------------
        coin.detachNode()

if __name__ == "__main__":
    # Initialize the world
    w = World()
    # run the game
    run()


